/*
 *  $Id: Tab.cs 574 2009-06-25 10:38:55Z thenn.erannor $
 *	This file is a part of Ragima CCG.
 *	(C) 2008 Ragima development group.
 *
 *	Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;

using AgateLib.InputLib;
using AgateLib.Geometry;

namespace Ragima {

    public enum TabsState {
        None,
        Show,
        Hide,
    }

    public delegate void TabEvent();

    public interface ITabSource {
        bool[] GetConditions(Player player);
        IEnumerable<TabContent> GetTabs(Player player);
    }

    /// <summary>
    /// Список табов, управляющий их поведением
    /// </summary>
    public class Tabs {
        private ITabSource _source;
        private bool[] _conditions;
        private List<Tab> _tabs;
        private Tab _tab;
        private TabsState _state;
        private int _numTab;

        public Tabs(Player player, ITabSource source) {
            _source = source;
            _conditions = source.GetConditions(player);
            _tabs = new List<Tab>();
            int i = 0;
            foreach (TabContent content in _source.GetTabs(player)) {
                _tabs.Add(new Tab(this, i, content));
                i++;
            }
            if (_tabs.Count > 0) {
                _tab = _tabs[0];
                _tab.Content.OnSelect();
            }
        }

        public bool CompareSource(Player player, ITabSource source) {
            if (!object.ReferenceEquals(source, _source)) return false;
            bool[] conditions = source.GetConditions(player);
            if (conditions.Length != _conditions.Length) return false;
            for (int i = 0; i < conditions.Length; i++) if (conditions[i] != _conditions[i]) return false;
            return true;
        }

        public int Count { get { return _tabs.Count; } }

        public Tab Current {
            get { return _tab; }
            set {
                if (_tab == value) return;
                if (_tab != null) _tab.Content.OnDeselect();
                _tab = value;
                if (_tab != null) _tab.Content.OnSelect();
                Game.Current.UpdateRegions();
            }
        }

        public void SelectTab(int num) {
            if (num >= 0 && num < _tabs.Count) Current = _tabs[num];
        }

        public event TabEvent ShowEvent;
        public event TabEvent HideEvent;

        /// <summary>
        /// Запустить процесс появления табов
        /// </summary>
        public void Show() {
            _state = TabsState.Show;
            _numTab = 0;
        }

        /// <summary>
        /// Запустить процесс появления табов, автоматически выбрав указанную вкладку
        /// </summary>
        public void Show(TabType type) {
            Show();
            foreach (Tab tab in _tabs) {
                if (tab.Content.Type == type) {
                    Current = tab;
                    break;
                }
            }
        }

        /// <summary>
        /// Запустить процесс исчезновения табов
        /// </summary>
        public void Hide() {
            _state = TabsState.Hide;
            _numTab = _tabs.Count-1;
        }

        public IEnumerable<Region> GetRegions() {
            for (int i = _tabs.Count-1; i >= 0; i--) yield return _tabs[i].GetRegion();
        }

        /// <summary>
        /// Отрисовка всех табов
        /// </summary>
        public void Draw() {
            for (int i = _tabs.Count-1; i >= 0; i--) _tabs[i].Draw();
        }

        /// <summary>
        /// Обработка табов
        /// </summary>
        public void Process() {
            switch (_state) {
            case TabsState.Show:
                ProcessShow();
                break;
            case TabsState.Hide:
                ProcessHide();
                break;
            }
        }

        /// <summary>
        /// Обработка появления табов
        /// </summary>
        private void ProcessShow() {
            while (_numTab < _tabs.Count && _tabs[_numTab].IsShow) _numTab++;
            if (_numTab == _tabs.Count) {
                _state = TabsState.None;
                if (ShowEvent != null) ShowEvent();
            } else
                _tabs[_numTab].DoShow();
        }

        /// <summary>
        /// Обработка исчезновения табов
        /// </summary>
        private void ProcessHide() {
            while (_numTab >= 0 && _tabs[_numTab].IsHide) _numTab--;
            if (_numTab < 0) {
                _state = TabsState.None;
                if (HideEvent != null) HideEvent();
            } else
                _tabs[_numTab].DoHide();
        }

        /// <summary>
        /// Поиск содержимого таба конкретного типа
        /// </summary>
        /// <param name="condition">Условие поиска</param>
        /// <returns>Найденное содержимое, либо null</returns>
        public T FindContent<T>(Predicate<T> condition) where T: TabContent {
            T content;
            foreach (Tab tab in _tabs) {
                content = tab.Content as T;
                if (content != null && condition(content)) return content;
            }
            return null;
        }

        /// <summary>
        /// Поиск содержимого таба по его типу
        /// </summary>
        /// <param name="type">Тип содержимого</param>
        /// <returns>Найденное содержимое, либо null</returns>
        public TabContent FindContent(TabType type) {
            foreach (Tab tab in _tabs) if (tab.Content.Type == type) return tab.Content;
            return null;
        }
    }

    /// <summary>
    /// Отдельный абстрактный таб
    /// </summary>
    public class Tab {
        private const int MaxPos = Constants.TabHeight;

        private Tabs _parent;
        private int _num, _pos;
        private TabRegion _region;
        private TabContent _content;

        public Tab(Tabs parent, int num, TabContent content) {
            _parent = parent;
            _num = num;
            _pos = 0;
            _region = new TabRegion(this);
            _content = content;
        }

        public bool Selected {
            get { return _parent.Current == this; }
        }

        public int Number { get { return _num; } }
        public int Position { get { return _pos; } }
        public bool IsShow { get { return _pos == MaxPos; } }
        public bool IsHide { get { return _pos == 0; } }
        public TabContent Content { get { return _content; } }

        public void Select() {
            _parent.SelectTab(_num);
        }

        private int ShowStep { get { return (_parent.Count + 1); } }

        public void DoShow() {
            if (!IsShow) {
                _pos = Math.Min(_pos + ShowStep, MaxPos);
                UpdateRegion();
            }
        }
        public void DoHide() {
            if (!IsHide) {
                _pos = Math.Max(_pos - ShowStep, 0);
                UpdateRegion();
            }
        }

        public Region GetRegion() {
            UpdateRegion();
            return _region;
        }
        private void UpdateRegion() {
            Rectangle rect = _region.Rect;
            rect.Y = Constants.TabY + (MaxPos - _pos);
            rect.Height = _pos;
            _region.Rect = rect;
        }

        public void Draw() {
            if (IsHide) return;
            UI.Sprite sprite = Data.GetTab(_content.Type);
            Rectangle src = sprite.Descriptor.GetRect(_region.Focus ? 1 : 0);
            src.Height = _pos;
            Rectangle dest = _region.Rect;
            sprite.Surface.Draw(src, dest);
        }
    }

    /// <summary>
    /// Регион, определяющий таб (саму кнопку таба)
    /// </summary>
    public class TabRegion: Region {
        private Tab _tab;

        public TabRegion(Tab tab):
        base(new Rectangle(Constants.TabX + tab.Number*Constants.TabStep, Constants.TabY, Constants.TabWidth, Constants.TabHeight)) {
            _tab = tab;
            HotKey = KeyCode.D1 + tab.Number;
        }

        public override void OnClick() {
            _tab.Select();
        }
    }

    /// <summary>
    /// Абстрактное содержимое таба (но всегда являющееся списком картинок, возможно требующим скроллинга)
    /// </summary>
    public abstract class TabContent {
        private TabType _type;
        private bool _circular;
        private int _pos;

        public TabContent(TabType type, bool circular) {
            _type = type;
            _circular = circular;
        }

        public TabType Type { get { return _type; } }
        public bool Circular { get { return _circular; } }
        public int Position { get { return _pos < Count ? _pos : (_pos = 0); } }

        public virtual int Count { get { return 0; } }
        public virtual IEnumerable<Region> GetRegions() { yield break; }
        public virtual Region SelectedRegion { get { return null; } }

        public int CountFromPosition { get { return Math.Min(_circular ? Count : Count - Position, Constants.FieldSize); } }

        public int GetIndex(int index) {
            int count = Count;
            return count > 0 ? (_pos + index) % count : 0;
        }

        public bool NeedScrollLeft {
            get { return _circular ? Count > Constants.FieldSize : _pos > 0; }
        }
        public bool NeedScrollRight {
            get { return _circular ? Count > Constants.FieldSize : _pos < (Count - Constants.FieldSize); }
        }

        public void ScrollLeft() {
            if (NeedScrollLeft) {
                _pos--;
                if (_pos < 0) _pos = Count-1;
                OnScroll();
            }
        }
        public void ScrollRight() {
            if (NeedScrollRight) {
                _pos++;
                if (_pos == Count) _pos = 0;
                OnScroll();
            }
        }

        public virtual void OnSelect() {}
        public virtual void OnDeselect() {}
        public virtual void OnScroll() {}
    }
}